<?php
	/**
	*	This is a PHP 4/5 library for connecting the Master-Unlock API (v1)
	*
	*	It supposes that you have cURL installed.
	*	If it is not the case, please ask your system administrator.
	*	More information : http://www.php.net/curl
	*
	*	If you need assistance, please contact info@master-unlock.com
	*/

	/* Enter your API key here */
	define ('MASTERUNLOCK_API_KEY', '4CA1-1279-4961-F681-6EF1-3EC1-8CA1-8DF0'); // 168-2
	
	/* Set this value to TRUE if something goes wrong and you want to display error messages */
	define ('MASTERUNLOCK_API_DEBUG', FALSE);
	
	/* This is the url of the api, don't change it */

	//define ('MASTERUNLOCK_API_URL', 'http://master-unlock.com/api/v1/');
	define ('MASTERUNLOCK_API_URL', 'http://localhost/api.master-unlock/server/v1/api.php');
	
	/**
	*	To allow MasterUnlock class functions to be called statically - e.g. 
	*	MasterUnlock::Function() and to keep backward compatibility with PHP4, 
	*	some variables to be shared between	member functions will be allocated globally. 
	*	Their names are listed below.
	*	This is just for information, as they almost certainly won't interact 
	*	in any way with your scripts.
	*	If you did not understand the above, this is not important.
	*	
	*	For the same reason (backward compatibility), you may experience 
	*	warnings such as :
	*	Strict standards : Non-static method MasterUnlock:: ... () should not 
	*	be called statically.
	*	In this case, change your error reporting directive to remove the 
	*	E_STRICT level, Or, if this is not acceptable to you, add a static 
	*	keyword in front of each function name in this file.
	*/
	
	define ('MASTERUNLOCK_VARIABLE_ERROR',    '_MasterUnlockError'    );
	define ('MASTERUNLOCK_VARIABLE_ARRAY',    '_MasterUnlockArray'    );
	define ('MASTERUNLOCK_VARIABLE_POINTERS', '_MasterUnlockPointers' );

	
	/* Check that cURL is installed */
	
	if (! extension_loaded('curl')) {
		trigger_error ('cURL extension not installed', E_USER_ERROR);
	}
	
	
	class MasterUnlock {
		/**
		*	mixed MasterUnlock::CallAPI (string $command, array $parametersList)
		*	Call the MasterUnlock API.
		*	Returns the xml stream sent by the MasterUnlock server
		*	Or FALSE if an error occurs
		*/
		function CallAPI ( $command, $parametersList = array() ) {
			if (is_string($command)) {
				if (is_array($parametersList)) {
					/* Add the API Key and the command to the parametersList */
					$parametersList['KEY'] = MASTERUNLOCK_API_KEY;
					$parametersList['COMMAND'] = $command;

					/* Prepare the cURL session */
					$Ch = curl_init(MASTERUNLOCK_API_URL);		
					curl_setopt($Ch, CURLOPT_CONNECTTIMEOUT, 10);
					curl_setopt($Ch, CURLOPT_TIMEOUT, 30);
					curl_setopt($Ch, CURLOPT_HEADER, FALSE);
					curl_setopt($Ch, CURLOPT_RETURNTRANSFER, TRUE);
					curl_setopt($Ch, CURLOPT_ENCODING, '');
					curl_setopt($Ch, CURLOPT_POST, TRUE);
					curl_setopt($Ch, CURLOPT_POSTFIELDS, MasterUnlock::BuildQuery($parametersList));
					
					/* Perform the session */
					$Data = curl_exec($Ch);
					
					if (MASTERUNLOCK_API_DEBUG && curl_errno($Ch) != CURLE_OK) {
						/* If an error occurred, report it in debug mode */
						trigger_error (curl_error($Ch), E_USER_WARNING);
					}
					
					/* Close the session */
					curl_close($Ch);
					
					/* Return the data, or FALSE if an error occurred */
					return $Data;
				} else {
					trigger_error ('Parameters must be an array', E_USER_WARNING);
				}
			} else {
				trigger_error ('Command must be a string', E_USER_WARNING);
			}
			return FALSE;
		}


		/**
		*	mixed MasterUnlock::ParseXML (string $XML)
		*	Parse an XML stream from the MasterUnlock API.
		*	Returns an associative array of the parsed XML string
		*	Or FALSE if an error occurs
		*/
		function ParseXML ( $XML ) {
			if (! is_string($XML)) {
				/* If the argument is not a string, report the error in debug mode & stop here */
				if (MASTERUNLOCK_API_DEBUG) trigger_error ('Invalid argument supplied for MasterUnlock::ParseXML()', E_USER_WARNING);
				return FALSE;
			}

			/* Globalize variables */
			global ${MASTERUNLOCK_VARIABLE_ERROR}    ;
			global ${MASTERUNLOCK_VARIABLE_ARRAY}    ;
			global ${MASTERUNLOCK_VARIABLE_POINTERS} ;

			/* Initialize variables */
			${MASTERUNLOCK_VARIABLE_ERROR}    = FALSE   ;
			${MASTERUNLOCK_VARIABLE_ARRAY}    = array() ;
			${MASTERUNLOCK_VARIABLE_POINTERS} = array() ;

			/* Configure the parser */
			$Parser = xml_parser_create('UTF-8');
			xml_set_element_handler($Parser, array('MasterUnlock', 'XML_Start'), array('MasterUnlock', 'XML_End'));
			xml_set_character_data_handler($Parser, array('MasterUnlock', 'XML_CData'));
			xml_parser_set_option($Parser, XML_OPTION_CASE_FOLDING, 0);
			
			/* Start parsing, check the success of both parsing and analyzing */
			$Success = xml_parse($Parser, $XML, TRUE) && ! ${MASTERUNLOCK_VARIABLE_ERROR};
			
			/* Report errors in debug mode */
			if (MASTERUNLOCK_API_DEBUG) {
				if (${MASTERUNLOCK_VARIABLE_ERROR}) {
					/* The XML stream has not been recognized */
					trigger_error ('Unrecognized XML format', E_USER_WARNING);
				} elseif (xml_get_error_code($Parser) != XML_ERROR_NONE) {
					/* A parser error occurred */
					trigger_error (xml_error_string(xml_get_error_code($Parser)), E_USER_WARNING);
				}
			}

			/* Free the parser */
			xml_parser_free($Parser);
			
			/* Get a reference to the result */
			$Array =& ${MASTERUNLOCK_VARIABLE_ARRAY};
			
			/* Unset global variables */
			unset ( $GLOBALS[MASTERUNLOCK_VARIABLE_ERROR]    );
			unset ( $GLOBALS[MASTERUNLOCK_VARIABLE_ARRAY]    );
			unset ( $GLOBALS[MASTERUNLOCK_VARIABLE_POINTERS] );

			/* Return the result */
			return ($Success ? $Array : FALSE);
		}



		/**
		*	bool MasterUnlock::CheckEmail (string $Email)
		*	Check the validity of an email address
		*	This function is *not* RFC 2822 compliant, but instead reflects today's email reality
		*	Returns TRUE if the email address seems correct, FALSE otherwise
		*/
		function CheckEmail ( $Email ) {
			return (bool) preg_match('/^[0-9a-z_\\-\\.]+@([0-9a-z][0-9a-z\\-]*[0-9a-z]\\.)+[a-z]{2,}$/i', $Email);
		}


		/**
		*	bool MasterUnlock::CheckIMEI (string $IMEI, bool $Checksum)
		*	Check a 15-digit IMEI serial number.
		*	You are free to verify the checksum, or not;
		*	Bad checksums are 99% likely to provide unavailable unlock 
		*	codes (exceptions exist, however)
		*	Returns TRUE if the IMEI seems correct, FALSE otherwise
		*/
		function CheckIMEI ( $IMEI, $Checksum = TRUE ) {
			if (is_string($IMEI)) {
				if (ereg('^[0-9]{15}$', $IMEI)) {
					if (! $Checksum) return TRUE;

					for ($i = 0, $Sum = 0; $i < 14; $i++) {
						$Tmp = $IMEI[$i] * ( ($i % 2) + 1 );
						$Sum += ($Tmp % 10) + intval($Tmp / 10);
					}
					
					return ( ( ( 10 - ( $Sum % 10 ) ) % 10 ) == $IMEI[14] );
				}
			}
			
			return FALSE;
		}
		

		/**
		*	bool MasterUnlock::CheckProviderID (string $ProviderID)
		*	Verify an Alcatel Provider ID
		*	Returns TRUE if the Provider ID seems correct, FALSE otherwise
		*/
		function CheckProviderID ( $ProviderID ) {
			return (is_string($ProviderID) && eregi('^[0-9a-z]{4,5}\\-[0-9a-z]{7}$', $ProviderID));
		}
		

		/**
		*	bool MasterUnlock::CheckMEP_PRD (string $Type, string $String)
		*	Check a MEP/PRD number before submitting it to the API
		*	$Type is either 'MEP' or 'PRD'
		*	Returns TRUE if the MEP/PRD seems correct, FALSE otherwise
		*/
		function CheckMEP_PRD( $Type, $String ) {
			return ereg('^' . $Type . '\\-[0-9]{5}\\-[0-9]{3}$', $String);
		}

	
		/* Internal functions - do not care */
		function BuildQuery ( $parametersList ) {
			if (function_exists('http_build_query')) { 
				/* PHP 5 */
				return http_build_query($parametersList);
			}
			else {
				/* PHP 4 */
				$Data = array();
				foreach ($parametersList as $Name => $Value) array_push($Data, urlencode($Name) . '=' . urlencode($Value));
				return implode('&', $Data);
			}
		}

		function XML_Start ( $Parser, $Name, $Attributes ) {
			/* Globalize variables */
			global ${MASTERUNLOCK_VARIABLE_ERROR};
			global ${MASTERUNLOCK_VARIABLE_ARRAY};
			global ${MASTERUNLOCK_VARIABLE_POINTERS};
			
			/* Do nothing if an error occurred previously */
			if (${MASTERUNLOCK_VARIABLE_ERROR}) return;

			if (count( ${MASTERUNLOCK_VARIABLE_POINTERS} ) == 0) {
				/* Root Element : create the first pointer to the array */
				${MASTERUNLOCK_VARIABLE_POINTERS}[] =& ${MASTERUNLOCK_VARIABLE_ARRAY};
			}
			else {
				/* Get the latest pointer */
				$Pointer =& ${MASTERUNLOCK_VARIABLE_POINTERS} [ count( ${MASTERUNLOCK_VARIABLE_POINTERS} ) -1 ];
				
				if (is_null($Pointer)) {
					/* This is the first sub-tag with that name, create the new container array for it */
					$Pointer[] = array();
					
					/* Replace the latest pointer, point to the first item of the new container */
					${MASTERUNLOCK_VARIABLE_POINTERS}[ count(${MASTERUNLOCK_VARIABLE_POINTERS}) -1 ] =& $Pointer[0];
					$Pointer =& $Pointer[0];
				} elseif (is_array($Pointer)) {
					if (isset($Pointer[$Name])) {
						if (! is_array($Pointer[$Name]))
						{
							/* Unrecognized XML stream */
							${MASTERUNLOCK_VARIABLE_ERROR} = TRUE;
							return;
						}
						
						/* The tag is already known, add an item to the array and create a pointer to it */
						$Pointer[$Name][] = array();
						${MASTERUNLOCK_VARIABLE_POINTERS}[] =& $Pointer[$Name][ count($Pointer[$Name]) -1 ];
						return;
					}
				} else {
					/* Unrecognized XML stream */
					${MASTERUNLOCK_VARIABLE_ERROR} = TRUE;
					return;
				}
				
				/* Set the default value and create a pointer to it */
				$Pointer[$Name] = NULL;
				${MASTERUNLOCK_VARIABLE_POINTERS}[] =& $Pointer[$Name];
			}
		}
		
		function XML_End ( $Parser, $Name ) {
			/* Globalize variables */
			global ${MASTERUNLOCK_VARIABLE_ERROR};
			global ${MASTERUNLOCK_VARIABLE_POINTERS};

			/* Do nothing if an error occurred previously */
			if (${MASTERUNLOCK_VARIABLE_ERROR}) return;

			/* Remove the latest pointer */
			array_pop( ${MASTERUNLOCK_VARIABLE_POINTERS} );
		}
		
		function XML_CData ( $Parser, $Data ) {
			/* Ignore whitespaces */
			if (rtrim($Data) == '') return;

			/* Globalize variables */
			global ${MASTERUNLOCK_VARIABLE_ERROR};
			global ${MASTERUNLOCK_VARIABLE_POINTERS};

			/* Do nothing if an error occurred previously */
			if (${MASTERUNLOCK_VARIABLE_ERROR}) return;
			
			/* Get the latest pointer */
			$Pointer =& ${MASTERUNLOCK_VARIABLE_POINTERS} [ count( ${MASTERUNLOCK_VARIABLE_POINTERS} ) -1 ];
			
			if (is_array($Pointer)) {
				/* Unrecognized XML stream, should be null or string here */
				${MASTERUNLOCK_VARIABLE_ERROR} = TRUE;
				return;
			}
			
			/* Append the character data */
			$Pointer .= $Data;
		}
	}